{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modLocalSettings;

interface

uses Classes,
     fmodtypes,
     SysUtils;




(****************************
Ce module gère le fichier de préférences communes à tous les profils
et en particulier l'url de téléchargement de news (chiffrée bien sur)
Le stream a une structure bien définie
************************)


const
    CNT_CURRENT_SETTING_VERSION : integer = 3;


    //////////////////////////
    // Index pour le type Integer

    // dans la liste prédéfinit des url de fichier de mirror
    // cet id correspond au dernier id de trouver valide
    CNT_LOCALSETTING_UpdateMirrorFileNextIdToStart = 0;

    CNT_LOCALSETTING_TunnelPort = 1;

    CNT_LOCALSETTING_BOOL_TunnelUseRegleReecriture = 2;
    CNT_LOCALSETTING_BOOL_TunnelRestreindreLocal   = 3;
    CNT_LOCALSETTING_BOOL_TunnelLoadAtStartup      = 4;
    CNT_LOCALSETTING_BOOL_TunnelDesactiveCompressionIE      = 5;


    CNT_LOCALSETTING_FMOD_Output  = 6;
    CNT_LOCALSETTING_FMOD_Driver  = 7;


    CNT_LOCALSETTING_BOOL_DL_NewsAUTO = 8;


    // On sauvegarde la version actuelle pour savoir quand es-ce que l'on a mis
    // a jour le robot
    CNT_LOCALSETTING_CurrVersion = 9;

    //////////////////////////
    // Index pour le type int64

    // date de validité du dernier chargement du fichier update, pour de temps en temps
    // mettre à jour les données qu'il contient
    // même si les données sont toujours valide, ainsi, en cas de fermeture d'un des
    // sites et que cette fermeture n'est pas détecté par le robot,
    // il y aura toujours moyen de mettre à jour l'url
    CNT_LOCALSETTING_UpdateFileLastDL  = 0;


    // Délai d'autorisation d'accès pour le forum
    CNT_LOCALSETTING_DATEAutorisation_Forum = 1;

    // Couple user/pass hashé en int64
    CNT_LOCALSETTING_KEYAutorisation_Forum  = 2;


    // Date de premier lancement
    CNT_LOCALSETTING_DATE_PremierLancement  = 3;

    // Date du dernier affichage de la licence
    CNT_LOCALSETTING_DATE_LastLicenceWizardShow  = 4;


    // Date du fichier de la licence qui a été accepté
    // dans le but de l'afficher à nouveau en cas de modification
    CNT_LOCALSETTING_DATE_LicenceDateAccepted  = 5;


    // Date de dernier téléchargement auto des news
    CNT_LOCALSETTING_DATE_LastAUTODlNews    = 6;


type
     TLocalSettingArrayInt   = array[0..64] of integer;
     TLocalSettingArrayInt64 = array[0..32] of int64;



     // pour les valeurs booleannes
     TLocalSettingArrayByte  = array[0..79] of byte;

     TLocalSettingShortString      = string[42];
     TLocalSettingArrayShortString = array[0..23] of TLocalSettingShortString;


     // futur header
     TLocalSettingHeader = record
         FileVersion : integer;
         ConfInteger : TLocalSettingArrayInt;
         ConfInt64   : TLocalSettingArrayInt64;
         ConfByte    : TLocalSettingArrayByte;
         ConfSString : TLocalSettingArrayShortString;
     end;



     TLocalSettingTypeToLoad = (lsoEnteteValues, lsoIniCryptedData, lsoIniData);
     TSLocalSettingTypeToLoad = set of TLocalSettingTypeToLoad;

     TLocalSetting = class(TObject)
     private
         FFichier: string;
         FLoadConfidentielData: boolean;

         FSettingFileVersion: integer;

         FCorrupted: boolean;

         // tableau de valeurs entières...
         ConfArrayInt                    : TLocalSettingArrayInt;
         ConfArrayInt64                  : TLocalSettingArrayInt64;


         function GetIniString: string;
         function GetIniStringSecure: string;

         procedure LoadLigne(Key, Valeur: string);
         function LoadIniFile(FichierStream: string): boolean;


         procedure Init;
         function KeyLoaded: boolean;


         //function SaveToStream(s: TStream): boolean;


         // charge les données des champs fixe (l'entête du fichier)
         function SaveToStreamFixedChamp(s: TStream): int64;

         // charge un fichier ini (le secure, ou le normal)
         // les deux fichiers sont cryptés par la clé IDEA
         function SaveToStreamIni(s: TStream;  CryptedData: boolean): boolean;

         // cette fonction se positionne là où il faut pour
         // sauvegarder la clé
         function SaveToStreamIDEAKey(s: TStream): boolean;


         // charge un fichier ini
         // il n'est pas nécessaire d'être bien positionné dans le stream
         // PAR CONTRE la clé IDEA doit être déjà chargé
         function LoadFromStreamIni(s: TStream; CryptedData: boolean): boolean;
         function LoadFromStreamIDEAKey(s: TStream): boolean;
         function LoadFromStreamFixed(s: TStream): boolean;

     public
         // charge le dernier profil au lancement du programme
         ConfLoadTheLastestProfilAtStarup: integer;

         // charge la derniere bd au chargement du profil
         ConfLoadTheLastestBDOnProfilLoad: integer;

         ConfSaveWebPage                 : integer;




         // Clé IDEA de chiffrage de la suite du stream
         IDEAKeySecure : string;
      //   IDEAKeyIni : string;

         // URL de téléchargement pour les news
         IniURLForNews: string;

     //    IniURLForMsg : string;

         // représente l'index du forum
         // exemple: http://site.com/
         // exemple: http://site.com/lqtech/
         IniURLBaseDirForForumPhpBB: string;


         // adresse de vérification d'accès au forum
         IniURL_ForumCheckAccess: string;


         // Date de dernier téléchargement des news de la base de données
         // cette date est utilisée lors du Post des informations
         IniNewsLastDateDL_BDD: int64;

         // date de dernier téléchargement des news par l'utilisateur
         // dans le but d'éviter de télécharger trop de fois les news
         IniNewsLastDateDL_Interne: int64;



         IniTunnelRootUser: string;
         IniTunnelRootPass: string;

         IniTunnelForumUser: string;
         IniTunnelForumPass: string;

         // Unique ID
         // aussi sauvegardé dans le registre
         IniIdentUniqueID: int64;

         // Mot de passe (aussi sauvegardé dans le registre)
         IniIdentPassword: string;

         IniDefaultServerLangageFile: string;


         // Dernier profil chargé
         IniLastProfilLoaded: string;

         // Pseudo sur l'IRC
         IRCNickname: string;
         IRCPassword: string;


         function ConfGet(Id: integer)  : integer;
         function ConfGet64(Id: integer): int64;
         function ConfGetBool(Id: integer): boolean;

         procedure ConfSet(Id, Valeur: integer);
         procedure ConfSetBool(Id: integer; Valeur: boolean);
         procedure ConfSet64(Id: integer; Valeur: int64);


         property Corrupted         : boolean read FCorrupted;
         property SettingFileVersion: integer read FSettingFileVersion;


         function LoadFromFile(Fichier: string; DataToLoad: TLocalSettingTypeToLoad): boolean;

         // charge les données cryptés (à partir du fichier FFichier)
         //function LoadFileIniSetting(InclureSecu: boolean): boolean;


         procedure BurnSecu;


       //  function LoadFromStreamAdv(TheCrypted: boolean; s: TFileStream): boolean;





         function SaveToFile(Fichier: string; DataToSave: TSLocalSettingTypeToLoad): boolean;

         constructor Create;
         destructor Destroy; override;

     end;


implementation

uses modInteractionKey,
     modInteractionAuteur;

     
const
   CNT_INI_LIGNE_SEPARATEUR = #$0A;

   // position du début de l'IDEAKey
   // plus exactement de la longeur des données cryptés (sur 4 bytes)
   // suivit de la clé IDEA
   CNT_BYTES_TO_SKIP_FOR_IDEADKEY : integer = (7 * SizeOf(Integer)) +
                                              SizeOf(TLocalSettingArrayInt) + SizeOf(TLocalSettingArrayInt64);

   CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA : integer = 4096;

   // SizeOf(Integer)
   CNT_MIN_FILE_SIZE_TOBE_VALID : integer = 4 + 4096;

function NeedToSaveKey(sv: TSLocalSettingTypeToLoad): boolean;
begin
    Result := false;

    if lsoIniCryptedData in sv then begin
       Result := true;
       exit;
    end;

    if lsoIniData in sv then begin
       Result := true;
       exit;
    end;


end;


function TLocalSetting.KeyLoaded: boolean;
begin
    Result := length(IDEAKeySecure) = CNT_IDEA_KEY_SIZE;

end;

procedure TLocalSetting.ConfSetBool(Id: integer; Valeur: boolean);
begin
    if Valeur then
       ConfSet(Id, 1)
    else
       ConfSet(Id, 0);
       
end;

procedure TLocalSetting.ConfSet(Id, Valeur: integer);
begin
    ConfArrayInt[Id] := Valeur;
end;

procedure TLocalSetting.ConfSet64(Id: integer; Valeur: int64);
begin
    ConfArrayInt64[Id] := Valeur;
end;

function TLocalSetting.ConfGetBool(Id: integer): boolean;
begin
   if ConfGet(Id) > 0 then
      Result := true
   else
      Result := false;
end;

function TLocalSetting.ConfGet(Id: integer)  : integer;
begin
    Result := ConfArrayInt[Id];
end;

function TLocalSetting.ConfGet64(Id: integer): int64;
begin
    Result := ConfArrayInt64[Id];
end;


procedure TLocalSetting.BurnSecu;
begin
    if IniIdentPassword <> '' then begin
       FillChar(IniIdentPassword[1], length(IniIdentPassword), #0);
       IniIdentPassword := '';
    end;

    if IniURLForNews <> '' then begin
       FillChar(IniURLForNews[1], length(IniURLForNews), #0);
       IniURLForNews := '';
    end;

    (*
    if IniURLForMsg <> '' then begin
       FillChar(IniURLForMsg[1], length(IniURLForMsg), #0);
       IniURLForMsg := '';
    end;
           *)
    if IniURLBaseDirForForumPhpBB <> '' then begin
       FillChar(IniURLBaseDirForForumPhpBB[1], length(IniURLBaseDirForForumPhpBB), #0);
       IniURLBaseDirForForumPhpBB := '';
    end;

    if IniURL_ForumCheckAccess <> '' then begin
       FillChar(IniURL_ForumCheckAccess[1], length(IniURL_ForumCheckAccess), 0);
       IniURL_ForumCheckAccess := '';
    end;

    IniIdentUniqueID := 0;

end;

procedure TLocalSetting.Init;
var i: integer;
begin
         // charge le dernier profil au lancement du programme
         ConfLoadTheLastestProfilAtStarup:= 0;

         // charge la derniere bd au chargement du profil
         ConfLoadTheLastestBDOnProfilLoad:= 1;

         ConfSaveWebPage                 := 0;


         // Clé IDEA de chiffrage de la suite du stream
         IDEAKeySecure := '';

         IniURL_ForumCheckAccess := '';

         // URL de téléchargement pour les news
         IniURLForNews := '';
         //IniURLForMsg  := '';
         IniURLBaseDirForForumPhpBB := '';

         // Unique ID
         // aussi sauvegardé dans le registre
         IniIdentUniqueID := 0;

         // Mot de passe (aussi sauvegardé dans le registre)
         IniIdentPassword := '';

         IniDefaultServerLangageFile:= '';


         IniNewsLastDateDL_BDD := 199;
         IniNewsLastDateDL_Interne := 583;

         IniTunnelRootUser := '';
         IniTunnelRootPass := '';

         IniTunnelForumUser := '';
         IniTunnelForumPass := '';


         IniLastProfilLoaded := '';

         for i := 0 to 64 do
             ConfArrayInt[i] := 0;

         for i := 0 to 32 do
             ConfArrayInt64[i] := 0;



end;

constructor TLocalSetting.Create;
begin
    inherited Create;

    Init;
end;

destructor TLocalSetting.Destroy;
begin

    inherited Destroy;
end;


function TLocalSetting.SaveToFile(Fichier: string; DataToSave: TSLocalSettingTypeToLoad): boolean;
label lFin;
var s: TFileStream;
begin

    if DataToSave = [] then begin
       // rien à sauvegarder
       Result := true;
       exit;
    end;

    Result := false;
    
    if Fichier = '' then
       Fichier := FFichier;

    try
       if FileExists(Fichier) = false then begin
          s := TFileStream.Create(Fichier, fmCreate);
       end else begin
          s := TFileStream.Create(Fichier, fmOpenWrite);
       end;

    except
       s := nil;
    end;

    if s = nil then begin
       Result := false;
       exit;
    end;

    if s.Size < CNT_MIN_FILE_SIZE_TOBE_VALID then begin
       // on sauvegarde tout
       if SaveToStreamFixedChamp(s) < 10 then begin
          Result := false;
       end else begin
          Result := true;
       end;

       if Result then begin
          IDEAKeySecure := iaGenerateKeyForIDEA;
          SaveToStreamIDEAKey(s);
          Result := SaveToStreamIni(s, true);

          if Result then
             Result := SaveToStreamIni(s, false);
       end;


    end else begin



       Result := true;

       try

          if NeedToSaveKey(DataToSave) then begin
             // on va avoir besoin de sauvegarder la clé

             if NoT(KeyLoaded) then begin
                // aucune clé n'existait
                // on la créé et on la sauvegarde
                IDEAKeySecure := iaGenerateKeyForIDEA;

                SaveToStreamIDEAKey(s);

             end else
                Result := true;

          end;

          if NoT(Result) then goto lFin;


          if lsoEnteteValues in DataToSave then begin
             // on sauvegarde  l'entête
             if SaveToStreamFixedChamp(s) < 10 then begin
                Result := false;
             end else begin
                Result := true;
             end;
          end;
          if NoT(Result) then goto lFin;


          if lsoIniCryptedData in DataToSave then begin
             Result := SaveToStreamIni(s, true);
          end;
          if NoT(Result) then goto lFin;


          if lsoIniData in DataToSave then begin
             Result := SaveToStreamIni(s, false);
          end;

lFin:

       except
          Result := false;
       end;

    end;



    s.Free;
end;

(*
function TLocalSetting.LoadFromStreamAdv(TheCrypted: boolean; s: TFileStream): boolean;
var sDataDeCrypt: string;
    lng: integer;
begin



    // recharge les données d'en tête
    // Edit: NON car elles ont peut être chargé sans
    //       quelles ne sont sauvegardées
    //if LoadFromStream(s) = false then begin
       // erreur de chargement de l'entête
    //   exit;
    //end;
    s.Seek(CNT_BYTES_TO_SKIP_FOR_IDEADKEY, soBeginning);


    // lit la longeur du stream crypté
    if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
       FCorrupted := true;
       s.Free;
       exit;
    end;


    // Ensuite il y a la clé
    SetLength(IDEAKeySecure, CNT_IDEA_KEY_SIZE);
    if s.Read(IDEAKeySecure[1], CNT_IDEA_KEY_SIZE) < CNT_IDEA_KEY_SIZE then begin
       FCorrupted := true;
       s.Free;
       exit;
    end;


    if TheCrypted then begin
       // on est déjà bien positionné

    end else begin
       s.Seek(CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA, soBeginning);

       // lit la longeur du stream crypté
       if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
          FCorrupted := true;
          s.Free;
          exit;
       end;
    end;


    // verifie la taille des données restantes
    if s.Size - s.Position < lng then begin
       // pas assez de données
       FCorrupted := true;
       s.Free;
       exit;
    end;


    // décrypte le stream
    sDataDeCrypt := iaIDEADecryptThisString(s, IDEAKeySecure[1], CNT_IDEA_KEY_SIZE);
    if length(sDataDeCrypt) < 8 then begin
       s.Free;
       FCorrupted := true;
       exit;
    end;

    
    // on peut à présent fermer le fichier
    s.Free;


    // et charge le fichier ini
    FLoadConfidentielData := TheCrypted;
    Result := LoadIniFile(sDataDeCrypt);

end;



function TLocalSetting.LoadFileIniSetting(InclureSecu: boolean): boolean;
var s: TFileStream;
    sDataDeCrypt: string;
    lng: integer;

begin

    Result := false;
    if FFichier = '' then exit;
    if FileExists(FFichier) = false then exit;


    try
       s := TFileStream.Create(FFichier, fmOpenRead);
    except
       s := nil;
    end;

    if s = nil then begin
       exit;
    end;

    if s.Size < 66 then begin
       s.Free;
       exit;
    end;

 //   Result := LoadFromStreamAdv(false, s);
    Result := LoadFromStreamIni(s, true);

    s.Free;
    s := nil;

end;
*)


const
    CNT_INI_URLForNews = 'URLForNews';
    CNT_INI_URLForMsg = 'URLForMsg';
    CNT_INI_IdentPassword = 'Idp';
    CNT_INI_IdentUniqueID = 'ghost';
    CNT_INI_DefaultServerLangageFile = 'dserveur';
    CNT_INI_NewsLastDateDL_BDD = 'ndatedlbdd';
    CNT_INI_NewsLastDateDL_Local = 'ndatedlloc';

    CNT_INI_URLBaseDirForForumPhpBB = 'xy64';

    CNT_INI_IniTunnelRootUser = 'rtu';
    CNT_INI_IniTunnelRootPass= 'rtp';

    CNT_INI_IniTunnelForumUser = 'ftux';
    CNT_INI_IniTunnelForumPass= 'ftpx';

    CNT_Ini_IniURL_ForumCheckAccess = 'fca';

    CNT_IniLastProfilLoaded = 'ilpl';

    CNT_INI_IRCNickname = 'rxn';
    CNT_INI_IRCPassword= 'rxp';




procedure TLocalSetting.LoadLigne(Key, Valeur: string);
begin
    if SameText(Key, CNT_INI_URLForNews) then begin
       if FLoadConfidentielData then
          IniURLForNews := Valeur;

          
    //end else if SameText(Key, CNT_INI_URLForMsg) then begin
    //   if FLoadConfidentielData then
    //      IniURLForMsg  := Valeur;

    end else if SameText(Key, CNT_INI_URLBaseDirForForumPhpBB) then begin
       if FLoadConfidentielData then
          IniURLBaseDirForForumPhpBB  := Valeur;

          
    end else if SameText(Key, CNT_Ini_IniURL_ForumCheckAccess) then begin
       if FLoadConfidentielData then
          IniURL_ForumCheckAccess  := Valeur;


    end else if SameText(Key, CNT_INI_IdentPassword) then begin
       if FLoadConfidentielData then
          IniIdentPassword  := Valeur;






    end else if SameText(Key, CNT_INI_IdentUniqueID) then begin
       if FLoadConfidentielData then begin
          try
             IniIdentUniqueID  := StrToInt(Valeur);
          except
             IniIdentUniqueID  := 0;
          end;
       end;

    end else if SameText(Key, CNT_INI_DefaultServerLangageFile) then begin
       IniDefaultServerLangageFile := Valeur;


    end else if SameText(Key, CNT_INI_IniTunnelRootUser) then begin
       IniTunnelRootUser := Valeur;

    end else if SameText(Key, CNT_INI_IniTunnelRootPass) then begin
       IniTunnelRootPass := Valeur;

    end else if SameText(Key, CNT_IniLastProfilLoaded) then begin
       IniLastProfilLoaded := Valeur;




    end else if SameText(Key, CNT_INI_IniTunnelForumUser) then begin
       IniTunnelForumUser := Valeur;

    end else if SameText(Key, CNT_INI_IniTunnelForumPass) then begin
       IniTunnelForumPass := Valeur;


    end else if SameText(Key, CNT_INI_IRCNickname) then begin
       IRCNickname := Valeur;
    end else if SameText(Key, CNT_INI_IRCPassword) then begin
       IRCPassword := Valeur;


    end else if SameText(Key, CNT_INI_NewsLastDateDL_BDD) then begin
          try
             IniNewsLastDateDL_BDD  := StrToInt(Valeur);
          except
             IniNewsLastDateDL_BDD  := 0;
          end;

    end else if SameText(Key, CNT_INI_NewsLastDateDL_Local) then begin
          try
             IniNewsLastDateDL_Interne  := StrToInt(Valeur);
          except
             IniNewsLastDateDL_Interne  := 0;
          end;

    end;

end;

function TLocalSetting.LoadIniFile(FichierStream: string): boolean;
var Ligne: string;
    xPos : integer;
    Key: string;
begin

    while (FichierStream <> '') do begin
        xPos := pos(CNT_INI_LIGNE_SEPARATEUR, FichierStream);

        if xPos > 0 then begin
           Ligne := copy(FichierStream, 1, xPos - 1);
           FillChar(FichierStream[1], xPos, #0);
           System.Delete(FichierStream, 1, xPos);
        end else begin
           Ligne := FichierStream;
           FillChar(FichierStream, length(FichierStream), #0);
           FichierStream := '';
        end;

        Ligne := trim(Ligne);
        if Ligne = '' then Continue;
        if Ligne[1] = ';' then Continue;
        if Ligne[1] = '/' then Continue;
        if Ligne[1] = '#' then Continue;


        xPos := pos('=', Ligne);
        if xPos = 0 then begin
           // impossible...
           Continue;
        end;

        Key := copy(Ligne, 1, xPos - 1);
        System.Delete(Ligne, 1, xPos);

        LoadLigne(Key, Ligne);

        if Ligne <> '' then
           FillChar(Ligne[1], length(Ligne), #0);

    end;

    Result := true;
    

end;

const CNT_FORMAT_STR = '%s=%s' + CNT_INI_LIGNE_SEPARATEUR;

function TLocalSetting.GetIniStringSecure: string;
begin
    Result := '';
    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_URLForNews, IniURLForNews]);
   // Result := Result + format(CNT_FORMAT_STR, [CNT_INI_URLForMsg, IniURLForMsg]);

    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_URLBaseDirForForumPhpBB, IniURLBaseDirForForumPhpBB]);




    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IdentPassword, IniIdentPassword]);

    Result := Result + CNT_INI_IdentUniqueID + '=' + IntToStr(IniIdentUniqueID) + CNT_INI_LIGNE_SEPARATEUR;

    Result := Result + CNT_INI_NewsLastDateDL_BDD   + '=' + IntToStr(IniNewsLastDateDL_BDD) + CNT_INI_LIGNE_SEPARATEUR;
    Result := Result + CNT_INI_NewsLastDateDL_Local + '=' + IntToStr(IniNewsLastDateDL_Interne) + CNT_INI_LIGNE_SEPARATEUR;

    Result := Result + format(CNT_FORMAT_STR, [CNT_Ini_IniURL_ForumCheckAccess, IniURL_ForumCheckAccess]);


end;

function TLocalSetting.GetIniString: string;
begin
    Result := '';

    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_DefaultServerLangageFile, IniDefaultServerLangageFile]);

    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IniTunnelRootUser, IniTunnelRootUser]);
    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IniTunnelRootPass, IniTunnelRootPass]);

    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IniTunnelForumUser, IniTunnelForumUser]);
    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IniTunnelForumPass, IniTunnelForumPass]);

    Result := Result + format(CNT_FORMAT_STR, [CNT_IniLastProfilLoaded   , IniLastProfilLoaded]);

    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IRCNickname, IRCNickname]);

    Result := Result + format(CNT_FORMAT_STR, [CNT_INI_IRCPassword   , IRCPassword]);


end;


function TLocalSetting.SaveToStreamIDEAKey(s: TStream): boolean;
begin
   Result := true;

   try
      // saute aussi de 4 octets (qui correspondent à la taille du stream crypté)
      s.Seek(CNT_BYTES_TO_SKIP_FOR_IDEADKEY + SizeOf(Integer), soBeginning);
   except
      Result := false;
   end;

   if NoT(Result) then
      exit;

   // ecrit la clé
   try
      if s.Write(IDEAKeySecure[1], CNT_IDEA_KEY_SIZE) < CNT_IDEA_KEY_SIZE then
         Result := false;

   except
      Result := false;
   end;


end;

function TLocalSetting.SaveToStreamFixedChamp(s: TStream): int64;
var lng: integer;
begin


    Result := 0;


    try
       ////////////////////////
       // Ecriture de l'entête (7 fois 4 octets)
       ////////////////////////
       s.Write(CNT_CURRENT_SETTING_VERSION, SizeOf(FSettingFileVersion));
       s.Write(ConfLoadTheLastestProfilAtStarup, SizeOf(ConfLoadTheLastestProfilAtStarup));
       s.Write(ConfLoadTheLastestBDOnProfilLoad, SizeOf(ConfLoadTheLastestBDOnProfilLoad));
       s.Write(ConfSaveWebPage, SizeOf(ConfSaveWebPage));


       // pour un usage futur
       lng := 0;
       s.Write(lng, SizeOf(lng));
       s.Write(lng, SizeOf(lng));
       s.Write(lng, SizeOf(lng));


       s.Write(ConfArrayInt  , SizeOf(TLocalSettingArrayInt));
       s.Write(ConfArrayInt64, SizeOf(TLocalSettingArrayInt64));




       ////////////////////////////////////////
       // Ne fait pas partie de l'entête:
       // prepare la place pour les données chiffrées
       Result := s.Position;
       s.Write(lng, SizeOf(lng));



    except
       Result := 0;
    end;

end;

function TLocalSetting.LoadFromStreamIDEAKey(s: TStream): boolean;
var lng: integer;
begin

    Result := false;

    s.Seek(CNT_BYTES_TO_SKIP_FOR_IDEADKEY, soBeginning);

    // lit la longeur du stream chiffré
    if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
       FCorrupted := true;
       exit;
    end;


    // Ensuite il y a la clé
    SetLength(IDEAKeySecure, CNT_IDEA_KEY_SIZE);
    if s.Read(IDEAKeySecure[1], CNT_IDEA_KEY_SIZE) < CNT_IDEA_KEY_SIZE then begin
       FCorrupted := true;
       exit;
    end;

    Result := true;
    
end;



function TLocalSetting.LoadFromStreamIni(s: TStream; CryptedData: boolean): boolean;
var lng:  integer;
    sDataDeCrypt: string;
begin

    Result := false;
    
    if CryptedData then begin
       s.Seek(CNT_BYTES_TO_SKIP_FOR_IDEADKEY, soBeginning);

    end else begin
       s.Seek(CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA, soBeginning);

    end;

    // lit la longeur du stream chiffré
    if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
       FCorrupted := true;
       exit;
    end;

    if CryptedData then begin
       // saute la clé IDEA
       s.Seek(CNT_IDEA_KEY_SIZE, soCurrent);
    end;


    // verifie la taille des données restantes
    if s.Size - s.Position < lng then begin
       // pas assez de données
       FCorrupted := true;
       exit;
    end;

    
    // décrypte le stream
    sDataDeCrypt := iaIDEADecryptThisString(s, IDEAKeySecure[1], CNT_IDEA_KEY_SIZE);
    if length(sDataDeCrypt) < 5 then begin
       FCorrupted := true;
       exit;
    end;


    // et charge le fichier ini
    FLoadConfidentielData := CryptedData;
    Result := LoadIniFile(sDataDeCrypt);

end;


function TLocalSetting.SaveToStreamIni(s: TStream;CryptedData: boolean): boolean;
var lng: integer;
    sDataToCrypt: string;
    PositionToWriteLengthData: integer;
begin

    Result := true;
    
    // la suite du fichier binaire est un fichier de type INI
    // qui est compressé et crypté par IDEA (sans RSA)
    if CryptedData then begin

       PositionToWriteLengthData := CNT_BYTES_TO_SKIP_FOR_IDEADKEY;

       
       try
          s.Seek(CNT_BYTES_TO_SKIP_FOR_IDEADKEY + SizeOf(integer) + CNT_IDEA_KEY_SIZE, soBeginning);
       except
          Result := false;
       end;

       if NoT(Result) then
          exit;

       sDataToCrypt := GetIniStringSecure;

    end else begin

       PositionToWriteLengthData := CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA;


       lng := 0;
       while s.Size < (CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA + SizeOf(integer)) do begin
           try
             s.Write(lng, SizeOf(lng));
           except
             Result := false;
           end;

           if NoT(Result) then break;

       end;
       
       if NoT(Result) then
          exit;

       try
          s.Seek(CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA + SizeOf(lng), soBeginning);
       except
          Result := false;
       end;

       if NoT(Result) then
          exit;

       sDataToCrypt := GetIniString;

    end;


    lng := s.Position;

    try
       // ecrit le stream crypté
       iaIDEACrypteThisString(sDataToCrypt, IDEAKeySecure[1], CNT_IDEA_KEY_SIZE, s);
    except
       Result := false;
    end;


    if NoT(Result) then
       exit;

    lng := s.Position - lng;

    
    try
       s.Seek(PositionToWriteLengthData, soBeginning);
       s.Write(lng, sizeOf(lng));
       s.Seek(0, soEnd);
       
    except
       Result := false;
    end;
end;

(*
function TLocalSetting.SaveToStream(s: TStream): boolean;
var lng: integer;
    backup_pos: int64;
begin

    Result := true;

    backup_pos := SaveToStreamFixedChamp(s);

    if backup_pos < 10 then begin
       // Erreur
       Result := false;
       exit;
    end;


    if length(IDEAKeySecure) <> CNT_IDEA_KEY_SIZE then begin
       IDEAKeySecure := iaGenerateKeyForIDEA;

       ////////////////////////
       // Ecriture de la clé (64 octets)
       ////////////////////////
       try
          s.Write(IDEAKeySecure[1], CNT_IDEA_KEY_SIZE);
       except
          Result := false;
       end;

    end else begin
       // sinon saute cette section
       try
          s.Seek(CNT_IDEA_KEY_SIZE, soCurrent);
       except
          Result := false;
       end;

    end;



    if NoT(Result) then
       exit;

    SaveToStreamIni(s, true);

    lng := 0;

    while s.Size < (CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA + SizeOf(lng)) do begin
        try
          s.Write(lng, SizeOf(lng));
        except
          Result := false;
        end;

        if NoT(Result) then break;

    end;

    s.Seek(CNT_BYTES_TO_SKIP_FOR_NON_SECURE_DATA + SizeOf(lng), soBeginning);
    SaveToStreamIni(s, false);


    Result := true;

end;
*)


function TLocalSetting.LoadFromStreamFixed(s: TStream): boolean;
var lng: integer;
begin


    Result := false;
    FCorrupted := false;


       ////////////////////////
       // Lecture de l'entête (7 fois 4 octets)
       ////////////////////////

    try
       if s.Read(FSettingFileVersion, SizeOf(FSettingFileVersion)) < SizeOf(FSettingFileVersion) then begin
          exit;
       end;

       FCorrupted := true;

       if s.Read(ConfLoadTheLastestProfilAtStarup, SizeOf(ConfLoadTheLastestProfilAtStarup)) < SizeOf(ConfLoadTheLastestProfilAtStarup) then begin
          exit;
       end;

       if s.Read(ConfLoadTheLastestBDOnProfilLoad, SizeOf(ConfLoadTheLastestBDOnProfilLoad)) < SizeOf(ConfLoadTheLastestBDOnProfilLoad) then begin
          exit;
       end;

       if s.Read(ConfSaveWebPage, SizeOf(ConfSaveWebPage)) < SizeOf(ConfSaveWebPage) then begin
          exit;
       end;

       // pour un usage futur
       if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
          exit;
       end;
       if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
          exit;
       end;
       if s.Read(lng, SizeOf(lng)) < SizeOf(lng) then begin
          exit;
       end;


       if s.Read(ConfArrayInt, SizeOf(TLocalSettingArrayInt)) < SizeOf(TLocalSettingArrayInt) then begin
          exit;
       end;

       if s.Read(ConfArrayInt64, SizeOf(TLocalSettingArrayInt64)) < SizeOf(TLocalSettingArrayInt64) then begin
          exit;
       end;


    except

    end;

    if FSettingFileVersion < 3 then begin
       ConfSet(CNT_LOCALSETTING_FMOD_Output, Ord(FSOUND_OUTPUT_DSOUND));
       ConfSet(CNT_LOCALSETTING_FMOD_Driver, 0);
    end;


    FCorrupted := false;
    Result := true;
end;

function TLocalSetting.LoadFromFile(Fichier: string; DataToLoad: TLocalSettingTypeToLoad): boolean;
var s: TFileStream;
begin

    Result := false;
    if Fichier = '' then
       Fichier := FFichier
    else
       FFichier := Fichier;



       
    if FileExists(Fichier) = false then exit;

    try
       s := TFileStream.Create(Fichier, fmOpenRead);
    except
       s := nil;
    end;


    if s = nil then begin
       exit;
    end;

    if s.Size < CNT_MIN_FILE_SIZE_TOBE_VALID then begin
       // fichier trop petit
       // invalide
       s.Free;
       Result := false;
       exit;
    end;


    try
       if DataToLoad <> lsoEnteteValues then begin
          // on va avoir besoin de la clé IDEA
          if NoT(KeyLoaded) then begin
             Result := LoadFromStreamIDEAKey(s);

          end else
             Result := true;
             

          if Result then begin
             Result := LoadFromStreamIni(s, DataToLoad = lsoIniCryptedData);
          end;

       end else begin
          Result := LoadFromStreamFixed(s);
       end;


    finally
       s.Free;
    end;

end;

end.
